#ifndef TRACE_H
#define TRACE_H

#include "general.h"
#include "constants.h"
#include <ctime>
#include <vector>
#include <deque>
#include <fstream>

using namespace std;

struct Trace
{
	int nodeNum;

	Position nodes_position[MAX_NODE];
	int nodes_id[MAX_NODE];
	deque<Traffic> nodes_traffic;

	void printNodes(string filename)
	{
		ofstream fout(filename.c_str());
		fout << nodeNum << endl;
		for (int i = 0; i < nodeNum; i++)
		{
			fout << nodes_id[i] << " " << nodes_position[i].x << " " << nodes_position[i].y << endl;
		}
		fout.close();
		//printNodes(nodeNum);
	}

	void printNodes(int nodeCount)
	{
		cout << nodeCount << endl;
		for (int i = 0; i < nodeCount; i++)
		{
			cout << nodes_id[i] << " " << nodes_position[i].x << " " << nodes_position[i].y << " ";
			int nbCount = 0;
			for (int j = 0; j < nodeCount; j++)
				if (nodes_position[i].isNeighbor(nodes_position[j]))
					nbCount++;
			cout << nbCount << endl;
		}
	}

	bool generateNodesFromFile(string filename)
	{
		ifstream fin (filename.c_str());
		fin >> nodeNum;
		for (int i = 0; i < nodeNum; i++)
		{
			fin >> nodes_id[i] >> nodes_position[i].x >> nodes_position[i].y;
		}
		fin.close();
		return true;
	}

	bool generateNodes(int nodeNum, string filename = "")
	{
		this->nodeNum = nodeNum;
		int nodeCount = 0;
		srand((unsigned int)time(NULL));		
		while (nodeCount < nodeNum)
		{
			bool done = false;

			// if try too many times, just quit and return false
			int tryCount = 0;
			do
			{
				tryCount++;

				int x = rand() % MAX_X;		//numAP = MIN_AP + (int)(rand() % (MAX_AP - MIN_AP + 1));
				int y = rand() % MAX_Y;
				int availableID[MAX_ID];
				memset(availableID, 0, sizeof(availableID));

				bool haveNeighbor = false || nodeCount == 0;	// the node must have a neighbor to transmit data

				// find the neighbors of (x, y)
				for (int i = 0; i < nodeCount; i++)
				{
					if (nodes_position[i].isNeighbor(x, y))
					{
						// find the contention domain of i
						for (int j = 0; j < nodeCount; j++)
						{
							if (nodes_position[j].isNeighbor(nodes_position[i]))
							{
								availableID[nodes_id[j]] = 1;
								haveNeighbor = true;
							}
						}
					}
				}

				if (!haveNeighbor)			// need to retry
					continue;

				// find a free ID for (x, y)
				for (int i = 0; i < MAX_ID; i++)
				{
					if (availableID[i] == 0)
					{
						// get an ID for (x, y)
						nodes_id[nodeCount] = i;
						nodes_position[nodeCount].setXY(x, y);
						done = true;
						break;
					}
				}

				// not done yet, try another (x, y)
				// or exceed the maxmum number of try
				if (tryCount > MAX_TRY)
					break;
			} while (done == false);

			// failed, just return false
			if (done == false)
			{
				printNodes(nodeCount);
				return false;
			}

			// done for the previous node
			nodeCount++;
		}
		if (filename.length() > 0)
		{
			printNodes(filename);
		}
		return true;
	}

	// get a random neighbor of node
	int getRandomNeighbor(int node)
	{
		vector<int> neighborList;
		for (int i = 0; i < nodeNum; i++)
		{
			if (node != i && nodes_position[i].isNeighbor(nodes_position[node]))
				neighborList.push_back(i);
		}

		srand((unsigned int)time(NULL));
		int randomIndex = rand() % (int)neighborList.size();
		return neighborList[randomIndex];
	}

	// generate traffic within maxTime (in millisecond) of bitrate
	// bitrate: # of packets in a second
	void generateTraffic(int maxTime, int bitrate, int unit = 1, string filename = "")
	{
		// at the first time slot, every node is trying to send several(unit) packet to a random neighbor
		for (int i = 0; i < nodeNum; i++)
		{
			int j = getRandomNeighbor(i);
			Traffic tr(0, i, j, unit);
			nodes_traffic.push_back(tr);
		}

		// at other per 1000 time slots, the probability of unit packet is bitrate / 1000 / unit
		//srand(12345);	
		//for (int t = 1; t < maxTime; t++)	// 10^-3s
		//{
		//	for (int i = 0; i < nodeNum; i++)
		//	{
		//		double prob = (double)rand() / (double)RAND_MAX;
		//		if (prob <= (double)bitrate / 1000 / unit)
		//		{
		//			int j = getRandomNeighbor(i);
		//			Traffic tr(t * 1000, i, j, unit);
		//			nodes_traffic.push_back(tr);
		//		}
		//	}
		//}
		if (filename.length() > 0)
		{
			printTraffic(filename);
		}
	}

	bool generateTraceForT2F(int nodeNum, string nodefile, string tracefile)
	{
		this->nodeNum = nodeNum;
		int nodeCount = 0;
		unsigned int seed = (unsigned int)time(NULL);
		srand(seed);

		while (nodeCount < nodeNum)
		{
			bool done1 = false;
			bool done2 = false;

			// if try too many times, just quit and return false
			int tryCount = 0;
			do
			{
				done1 = false;
				done2 = false;
				tryCount++;

				int x1 = rand() % MAX_X;		//numAP = MIN_AP + (int)(rand() % (MAX_AP - MIN_AP + 1));
				int y1 = rand() % MAX_Y;
				int x2 = rand() % MAX_X;		//numAP = MIN_AP + (int)(rand() % (MAX_AP - MIN_AP + 1));
				int y2 = rand() % MAX_Y;
				int availableID1[MAX_ID];
				int availableID2[MAX_ID];
				memset(availableID1, 0, sizeof(availableID1));
				memset(availableID2, 0, sizeof(availableID2));

				if (!Position::isNeighbor(x1, y1, x2, y2))			// need to retry
					continue;

				// find the neighbors of (x1, y1)
				for (int i = 0; i < nodeCount; i++)
				{
					if (nodes_position[i].isNeighbor(x1, y1))
					{
						// find the contention domain of i
						for (int j = 0; j < nodeCount; j++)
						{
							if (nodes_position[j].isNeighbor(nodes_position[i]))
							{
								availableID1[nodes_id[j]] = 1;
							}
						}
					}
				}

				// find a free ID for (x1, y1)
				for (int i = 0; i < MAX_ID; i++)
				{
					if (availableID1[i] == 0)
					{
						// get an ID for (x1, y1)
						nodes_id[nodeCount] = i;
						nodes_position[nodeCount].setXY(x1, y1);
						done1 = true;
						break;
					}
				}

				// find the neighbors of (x2, y2)
				for (int i = 0; i < nodeCount + 1; i++)
				{
					if (nodes_position[i].isNeighbor(x2, y2))
					{
						// find the contention domain of i
						for (int j = 0; j < nodeCount + 1; j++)
						{
							if (nodes_position[j].isNeighbor(nodes_position[i]))
							{
								availableID2[nodes_id[j]] = 1;
							}
						}
					}
				}

				// find a free ID for (x2, y2)
				for (int i = 0; i < MAX_ID; i++)
				{
					if (availableID2[i] == 0)
					{
						// get an ID for (x2, y2)
						nodes_id[nodeCount + 1] = i;
						nodes_position[nodeCount + 1].setXY(x2, y2);
						done2 = true;
						break;
					}
				}

				// not done yet, try another (x, y)
				// or exceed the maxmum number of try
				if (tryCount > MAX_TRY)
					break;
			} while (done1 == false || done2 == false);

			// failed, just return false
			if (done1 == false || done2 == false)
			{
				printNodes(nodeCount);
				return false;
			}

			// done for the previous two node
			nodeCount += 2;
		}
		if (nodefile.length() > 0)
		{
			printNodes(nodefile);
		}

		nodes_traffic.clear();
		for (int i = 0; i < nodeNum; i++)
		{
			int j = 2 * (i / 2) + (1 - i % 2);
			Traffic tr(0, i, j, 3);
			if (!nodes_position[i].isNeighbor(nodes_position[j]))
			{
				cout << "error!" << endl;
				cout << seed << endl;
			}
			nodes_traffic.push_back(tr);
		}

		if (tracefile.length() > 0)
		{
			printTraffic(tracefile);
		}
		return true;		
	}

	void printTraffic(string filename)
	{
		ofstream fout(filename.c_str());
		fout << (int)nodes_traffic.size() << endl;
		for (deque<Traffic>::iterator it = nodes_traffic.begin(); it != nodes_traffic.end(); it++)
		{
			fout << it->time_slot << " " << it->sender_id << " " << it->receiver_id << " " << it->pkt_number << endl;
		}
		fout.close();
	}

	bool generateTrafficFromFile(string filename)
	{
		ifstream fin (filename.c_str());
		int trafficSize;
		fin >> trafficSize;
		for (int i = 0; i < trafficSize; i++)
		{
			int sender;
			int receiver;
			int time;
			int pkt;
			fin >> time >> sender >> receiver >> pkt;
			Traffic tr(time, sender, receiver, pkt);
			nodes_traffic.push_back(tr);
		}
		fin.close();
		return true;
	}

};

#endif